package com.simen.hellobytes.encrypt;

import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;

import static com.simen.hellobytes.encrypt.CipherEncryption.Algorithm.AES;
import static com.simen.hellobytes.encrypt.CipherEncryption.Mode.CBC;
import static com.simen.hellobytes.encrypt.CipherEncryption.PKCS.PKCS5Padding;

/**
 * author: SimenHi
 * create time: 2020-12-31 14:11
 * description:
 */
public class AesUtil {

    public static final String KEY_ALGORITHM = "AES";
    public static final String CIPHER_CBC_ALGORITHM = "AES/CBC/PKCS5Padding";
    public static final String CIPHER_ECB_P5_ALGORITHM = "AES/ECB/PKCS5Padding";
    public static final String CIPHER_ECB_P7_ALGORITHM = "AES/ECB/PKCS7Padding";

    /**
     * @param length 128,192,256
     * @return
     * @throws NoSuchAlgorithmException
     */
    public static SecretKey generateKey(int length) throws NoSuchAlgorithmException {
        return CipherEncryption.generateKey(AES, length);
    }

    public static SecretKey getKey(byte[] aesKey) {
        return CipherEncryption.getKey(AES, aesKey);
    }

    public static SecretKey getKeyFromPassword(String password, String salt)
            throws NoSuchAlgorithmException, InvalidKeySpecException {
        SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
        KeySpec spec = new PBEKeySpec(password.toCharArray(), salt.getBytes(), 65536, 256);
        SecretKey secret = new SecretKeySpec(factory.generateSecret(spec).getEncoded(), KEY_ALGORITHM);
        return secret;
    }

    public static IvParameterSpec generateIv() {
        return CipherEncryption.generateIv();
    }

    public static byte[] encrypt(Key key, byte[] plainByte) throws NoSuchPaddingException,
            NoSuchAlgorithmException,
            BadPaddingException,
            IllegalBlockSizeException,
            InvalidKeyException, InvalidAlgorithmParameterException {
        return CipherEncryption.newInstance().setKey(key).setAlgorithm(AES).setMode(CBC).setPkcs(PKCS5Padding).encrypt(plainByte);
    }

    public static byte[] encrypt(Key key, byte[] plainByte, String mode) throws NoSuchPaddingException,
            NoSuchAlgorithmException,
            BadPaddingException,
            IllegalBlockSizeException,
            InvalidKeyException, InvalidAlgorithmParameterException {
        return CipherEncryption.newInstance().setKey(key).setAlgorithmMode(mode).encrypt(plainByte);
    }

    public static byte[] decrypt(Key key, byte[] cipherByte) throws NoSuchPaddingException,
            NoSuchAlgorithmException,
            BadPaddingException,
            IllegalBlockSizeException,
            InvalidKeyException, InvalidAlgorithmParameterException {
        return CipherEncryption.newInstance().setKey(key).setAlgorithm(AES).setMode(CBC).setPkcs(PKCS5Padding).decrypt(cipherByte);
    }

    public static byte[] decrypt(Key key, byte[] cipherByte, String mode) throws NoSuchPaddingException,
            NoSuchAlgorithmException,
            BadPaddingException,
            IllegalBlockSizeException,
            InvalidKeyException, InvalidAlgorithmParameterException {
        return CipherEncryption.newInstance().setKey(key).setAlgorithmMode(mode).decrypt(cipherByte);
    }

    public static byte[] encrypt(byte[] plainByte, SecretKey key,
                                 IvParameterSpec iv) throws NoSuchPaddingException, NoSuchAlgorithmException,
            InvalidAlgorithmParameterException, InvalidKeyException,
            BadPaddingException, IllegalBlockSizeException {
        return CipherEncryption.newInstance().setKey(key).setAlgorithm(AES).setMode(CBC).setPkcs(PKCS5Padding).setIv(iv).encrypt(plainByte);
    }

    public static byte[] decrypt(byte[] cipherText, SecretKey key,
                                 IvParameterSpec iv) throws NoSuchPaddingException, NoSuchAlgorithmException,
            InvalidAlgorithmParameterException, InvalidKeyException,
            BadPaddingException, IllegalBlockSizeException {
        return CipherEncryption.newInstance().setKey(key).setAlgorithm(AES).setMode(CBC).setPkcs(PKCS5Padding).setIv(iv).decrypt(cipherText);
    }

}
